﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace WooCoo.ObjectModel.ManagedProperty
{
    /// <summary>
    /// 托管属性元数据缓存，提高属性反复读取性能
    /// </summary>
    public class PropertyInfoRepository : IEnumerable<KeyValuePair<Type, PropertyInfoSet>>
    {
        #region SingleTon

        public static readonly PropertyInfoRepository Instance = new PropertyInfoRepository();

        private PropertyInfoRepository() { }

        #endregion

        #region 私有字段

        private object _cacheLock = new object();

        private Dictionary<Type, PropertyInfoSet> _propertyInfoCache = new Dictionary<Type, PropertyInfoSet>();

        private static MethodInfo method = typeof(IPropertyInfoFactory).GetMethod("Create", new Type[] { typeof(PropertyInfo) });
        //private ManagedPropertyLifeCycle _curLifeCycle = ManagedPropertyLifeCycle.CompileOrSetup;

        #endregion

        /// <summary>
        /// 从缓存中获取<see cref="PropertyInfoSet"/>对象，如找不到，则创建
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        private PropertyInfoSet GetOrCreateProperInfoSet(Type objectType)
        {
            PropertyInfoSet list = null;

            if (!this._propertyInfoCache.TryGetValue(objectType, out list))
            {
                list = new PropertyInfoSet(objectType);               
                this._propertyInfoCache.Add(objectType, list);
            }

            return list;
        }
        /// <summary>
        /// 取得类型及其基类的所有属性，直到基类为<see cref="ManagedPropertyBase"/>
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        internal PropertyInfoSet CreateConsolidatedProperInfoSet(Type objectType)
        {
            var baseType = objectType.BaseType;

            if (baseType != typeof(ManagedPropertyBase) && baseType != null)
            {
                this.CreateConsolidatedProperInfoSet(baseType);
            }

            return this.GetOrCreateProperInfoSet(objectType);
        }
        /// <summary>
        /// 取得指定类型的所有属性集
        /// </summary>
        /// <param name="ownerType"></param>
        /// <returns></returns>
        public PropertyInfoSet GetPropertyInfoSet(Type ownerType)
        {
            PropertyInfoSet list = this.GetOrCreateProperInfoSet(ownerType);
            //属性获取方法不在GetOrCreateProperInfoSet中是因为Entity需要实例化,这样在对角创建时有可能引起死循环
            if(list.GetProperties().Count()==0)
            {
                if (WooCoo.Tools.TypeHelper.IsTypeOfGenericBaseType(typeof(Entity),ownerType))
                {
                    //当未被缓存时，尝试实例化,使实体属性托管方法可被运行.
                    System.Activator.CreateInstance(ownerType);
                }
                else
                {
                    foreach (PropertyInfo pinfo in ownerType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
                    {
                        var p = method.MakeGenericMethod(pinfo.PropertyType).Invoke(PropertyInfoFactory.Factory,
                            new object[] { pinfo });
                        list.AddProperty(p as IPropertyInfo);
                        //PropertyInfo
                        //var p = PropertyInfoFactory.Factory.Create(ownerType, pinfo.Name, pinfo., pinfo.Name);
                    }
                } 
            }            
            return list;
        }

        /// <summary>
        /// 注册某个属性到容器中,在ManagedPropertyBase实现类中应用该方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="property"></param>
        /// <returns></returns>
        public PropertyInfo<T> RegisterProperty<T>(Type ownerType, PropertyInfo<T> property)
        {
            if (property.Index > 0)
            {
                throw new InvalidOperationException("同一个属性只能注册一次。");
            }
            var typeProperties = this.CreateConsolidatedProperInfoSet(ownerType);
            lock (typeProperties)
            {
                typeProperties.AddProperty(property);
            }

            return property;
        }

        public IEnumerator<KeyValuePair<Type, PropertyInfoSet>> GetEnumerator()
        {
            return this._propertyInfoCache.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

    }
}
